home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkTextIndex.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-07-16  |  16.5 KB  |  641 lines

  1. /* 
  2.  * tkTextIndex.c --
  3.  *
  4.  *    This module provides procedures that manipulate indices for
  5.  *    text widgets.
  6.  *
  7.  * Copyright 1992 Regents of the University of California.
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that the above copyright
  11.  * notice appear in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkTextIndex.c,v 1.2 92/07/16 16:32:26 ouster Exp $ SPRITE (Berkeley)";
  19. #endif
  20.  
  21. #include "default.h"
  22. #include "tkConfig.h"
  23. #include "tk.h"
  24. #include "tkText.h"
  25.  
  26. /*
  27.  * Forward declarations for procedures defined later in this file:
  28.  */
  29.  
  30. static void        BackwardChars _ANSI_ARGS_((TkText *textPtr,
  31.                 TkTextLine *linePtr, int *lineIndexPtr,
  32.                 int *chPtr, int count));
  33. static char *        ForwBack _ANSI_ARGS_((TkText *textPtr,
  34.                 char *string, int *lineIndexPtr, int *chPtr));
  35. static void        ForwardChars _ANSI_ARGS_((TkText *textPtr,
  36.                 TkTextLine *linePtr, int *lineIndexPtr,
  37.                 int *chPtr, int count));
  38. static char *        StartEnd _ANSI_ARGS_((TkText *textPtr,
  39.                 char *string, int *lineIndexPtr, int *chPtr));
  40.  
  41. /*
  42.  *----------------------------------------------------------------------
  43.  *
  44.  * TkTextGetIndex --
  45.  *
  46.  *    Given a string, return the line and character indices that
  47.  *    it describes.
  48.  *
  49.  * Results:
  50.  *    The return value is a standard Tcl return result.  If
  51.  *    TCL_OK is returned, then everything went well and information
  52.  *    is stored at *lineIndexPtr and *chPtr;  otherwise TCL_ERROR
  53.  *    is returned and an error message is left in interp->result.
  54.  *
  55.  * Side effects:
  56.  *    None.
  57.  *
  58.  *----------------------------------------------------------------------
  59.  */
  60.  
  61. int
  62. TkTextGetIndex(interp, textPtr, string, lineIndexPtr, chPtr)
  63.     Tcl_Interp *interp;        /* Use this for error reporting. */
  64.     TkText *textPtr;        /* Information about text widget. */
  65.     char *string;        /* Textual description of position. */
  66.     int *lineIndexPtr;        /* Store line number here. */
  67.     int *chPtr;            /* Store character position here. */
  68. {
  69.     register char *p;
  70.     char *end, *endOfBase;
  71.     TkTextLine *linePtr;
  72.     Tcl_HashEntry *hPtr;
  73.     TkAnnotation *markPtr;
  74.     TkTextTag *tagPtr;
  75.     TkTextSearch search;
  76.     int first;
  77.     char c;
  78.  
  79.     /*
  80.      *------------------------------------------------
  81.      * Stage 1: parse the base index.
  82.      *------------------------------------------------
  83.      */
  84.  
  85.     if (string[0] == '@') {
  86.     /*
  87.      * Find character at a given x,y location in the window.
  88.      */
  89.  
  90.     int x, y;
  91.  
  92.     p = string+1;
  93.     x = strtol(p, &end, 0);
  94.     if ((end == p) || (*end != ',')) {
  95.         goto error;
  96.     }
  97.     p = end+1;
  98.     y = strtol(p, &end, 0);
  99.     if (end == p) {
  100.         goto error;
  101.     }
  102.     *lineIndexPtr = TkBTreeLineIndex(TkTextCharAtLoc(textPtr, x,
  103.         y, chPtr));
  104.     endOfBase = end;
  105.     goto gotBase; 
  106.     } else if (isdigit(string[0]) || (string[0] == '-')) {
  107.     /*
  108.      * Base is identified with line and character indices.
  109.      */
  110.  
  111.     *lineIndexPtr = strtol(string, &end, 0) - 1;
  112.     if ((end == string) || (*end != '.')) {
  113.         goto error;
  114.     }
  115.     p = end+1;
  116.     if ((*p == 'e') && (strncmp(p, "end", 3) == 0)) {
  117.         linePtr = TkBTreeFindLine(textPtr->tree, *lineIndexPtr);
  118.         if (linePtr == NULL) {
  119.         Tcl_AppendResult(interp, "bad text index \"", string,
  120.             "\": no such line in text", (char *) NULL);
  121.         return TCL_ERROR;
  122.         }
  123.         *chPtr = linePtr->numBytes - 1;
  124.         endOfBase = p+3;
  125.         goto gotBase;
  126.     } else {
  127.         *chPtr = strtol(p, &end, 0);
  128.         if (end == p) {
  129.         goto error;
  130.         }
  131.         endOfBase = end;
  132.         goto gotBase;
  133.     }
  134.     }
  135.  
  136.     for (p = string; *p != 0; p++) {
  137.     if (isspace(*p) || (*p == '+') || (*p == '-')) {
  138.         break;
  139.     }
  140.     }
  141.     endOfBase = p;
  142.     if ((string[0] == 'e')
  143.         && (strncmp(string, "end", endOfBase-string) == 0)) {
  144.     /*
  145.      * Base position is end of text.
  146.      */
  147.  
  148.     *lineIndexPtr = TkBTreeNumLines(textPtr->tree) - 1;
  149.     linePtr = TkBTreeFindLine(textPtr->tree, *lineIndexPtr);
  150.     *chPtr = linePtr->numBytes - 1;
  151.     goto gotBase;
  152.     } else {
  153.     /*
  154.      * See if the base position is the name of a mark.
  155.      */
  156.  
  157.     c = *endOfBase;
  158.     *endOfBase = 0;
  159.     hPtr = Tcl_FindHashEntry(&textPtr->markTable, string);
  160.     *endOfBase = c;
  161.     if (hPtr != NULL) {
  162.         markPtr = (TkAnnotation *) Tcl_GetHashValue(hPtr);
  163.         *lineIndexPtr = TkBTreeLineIndex(markPtr->linePtr);
  164.         *chPtr = markPtr->ch;
  165.         goto gotBase;
  166.     }
  167.     }
  168.  
  169.     /*
  170.      * Nothing has worked so far.  See if the base has the form
  171.      * "tag.first" or "tag.last" where "tag" is the name of a valid
  172.      * tag.
  173.      */
  174.  
  175.     p = strchr(string, '.');
  176.     if (p == NULL) {
  177.     goto error;
  178.     }
  179.     if ((p[1] == 'f') && (endOfBase == (p+6))
  180.         && (strncmp(p+1, "first", endOfBase - (p+1)) == 0)) {
  181.     first = 1;
  182.     } else if ((p[1] == 'l') && (endOfBase == (p+5))
  183.         && (strncmp(p+1, "last", endOfBase - (p+1)) == 0)) {
  184.     first = 0;
  185.     } else {
  186.     goto error;
  187.     }
  188.     *p = 0;
  189.     hPtr = Tcl_FindHashEntry(&textPtr->tagTable, string);
  190.     *p = '.';
  191.     if (hPtr == NULL) {
  192.     goto error;
  193.     }
  194.     tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
  195.     TkBTreeStartSearch(textPtr->tree, 0, 0, TkBTreeNumLines(textPtr->tree),
  196.         0, tagPtr, &search);
  197.     if (!TkBTreeNextTag(&search)) {
  198.     Tcl_AppendResult(interp,
  199.         "text doesn't contain any characters tagged with \"",
  200.         Tcl_GetHashKey(&textPtr->tagTable, hPtr), "\"", (char *) NULL);
  201.     return TCL_ERROR;
  202.     }
  203.     if (first) {
  204.     *lineIndexPtr = search.line1;
  205.     *chPtr = search.ch1;
  206.     } else {
  207.     while (TkBTreeNextTag(&search)) {
  208.         *lineIndexPtr = search.line1;
  209.         *chPtr = search.ch1;
  210.     }
  211.     }
  212.  
  213.     /*
  214.      *-------------------------------------------------------------------
  215.      * Stage 2: process zero or more modifiers.  Each modifier is either
  216.      * a keyword like "wordend" or "linestart", or it has the form
  217.      * "op count units" where op is + or -, count is a number, and units
  218.      * is "chars" or "lines".
  219.      *-------------------------------------------------------------------
  220.      */
  221.  
  222.     gotBase:
  223.     p = endOfBase;
  224.     while (1) {
  225.     while (isspace(*p)) {
  226.         p++;
  227.     }
  228.     if (*p == 0) {
  229.         return TCL_OK;
  230.     }
  231.     
  232.     if ((*p == '+') || (*p == '-')) {
  233.         p = ForwBack(textPtr, p, lineIndexPtr, chPtr);
  234.     } else {
  235.         p = StartEnd(textPtr, p, lineIndexPtr, chPtr);
  236.     }
  237.     if (p == NULL) {
  238.         goto error;
  239.     }
  240.     }
  241.  
  242.     error:
  243.     Tcl_AppendResult(interp, "bad text index \"", string, "\"",
  244.         (char *) NULL);
  245.     return TCL_ERROR;
  246. }
  247.  
  248. /*
  249.  *----------------------------------------------------------------------
  250.  *
  251.  * TkTextPrintIndex --
  252.  *
  253.  *    Given a line number and a character index, this procedure
  254.  *    generates a string description of the position, which is
  255.  *    suitable for reading in again later.
  256.  *
  257.  * Results:
  258.  *    The characters pointed to by string are modified.
  259.  *
  260.  * Side effects:
  261.  *    None.
  262.  *
  263.  *----------------------------------------------------------------------
  264.  */
  265.  
  266. void
  267. TkTextPrintIndex(line, ch, string)
  268.     int line;            /* Line number. */
  269.     int ch;            /* Character position within line. */
  270.     char *string;        /* Place to store the position.  Must have
  271.                  * at least POS_CHARS characters. */
  272. {
  273.     sprintf(string, "%d.%d", line+1, ch);
  274. }
  275.  
  276. /*
  277.  *----------------------------------------------------------------------
  278.  *
  279.  * TkTextRoundIndex --
  280.  *
  281.  *    Given a line index and a character index, this procedure
  282.  *    adjusts those positions if necessary to correspond to the
  283.  *    nearest actual character within the text.
  284.  *
  285.  * Results:
  286.  *    The return value is a pointer to the line structure for
  287.  *    the line of the text's B-tree that contains the indicated
  288.  *    character.  In addition, *lineIndexPtr and *chPtr are
  289.  *    modified if necessary to refer to an existing character
  290.  *    in the file.
  291.  *
  292.  * Side effects:
  293.  *    None.
  294.  *
  295.  *----------------------------------------------------------------------
  296.  */
  297.  
  298.  
  299. TkTextLine *
  300. TkTextRoundIndex(textPtr, lineIndexPtr, chPtr)
  301.     TkText *textPtr;            /* Information about text widget. */
  302.     int *lineIndexPtr;            /* Points to initial line index,
  303.                      * which is overwritten with actual
  304.                      * line index. */
  305.     int *chPtr;                /* Points to initial character index,
  306.                      * which is overwritten with actual
  307.                      * character index. */
  308. {
  309.     int line, ch, lastLine;
  310.     TkTextLine *linePtr;
  311.  
  312.     line = *lineIndexPtr;
  313.     ch = *chPtr;
  314.     if (line < 0) {
  315.     line = 0;
  316.     ch = 0;
  317.     }
  318.     lastLine = TkBTreeNumLines(textPtr->tree) - 1;
  319.     if (line > lastLine) {
  320.     line = lastLine;
  321.     linePtr = TkBTreeFindLine(textPtr->tree, line);
  322.     ch = linePtr->numBytes - 1;
  323.     } else {
  324.     linePtr = TkBTreeFindLine(textPtr->tree, line);
  325.     if (ch < 0) {
  326.         ch = 0;
  327.     }
  328.     if (ch >= linePtr->numBytes) {
  329.         if (line == lastLine) {
  330.         ch = linePtr->numBytes - 1;
  331.         } else {
  332.         line++;
  333.         linePtr = TkBTreeNextLine(linePtr);
  334.         ch = 0;
  335.         }
  336.     }
  337.     }
  338.     *lineIndexPtr = line;
  339.     *chPtr = ch;
  340.     return linePtr;
  341. }
  342.  
  343. /*
  344.  *----------------------------------------------------------------------
  345.  *
  346.  * ForwBack --
  347.  *
  348.  *    This procedure handles +/- modifiers for indices to adjust
  349.  *    the index forwards or backwards.
  350.  *
  351.  * Results:
  352.  *    If the modifier is successfully parsed then the return value
  353.  *    is the address of the first character after the modifier, and
  354.  *    *lineIndexPtr and *chPtr are updated to reflect the modifier.
  355.  *    If there is a syntax error in the modifier then NULL is returned.
  356.  *
  357.  * Side effects:
  358.  *    None.
  359.  *
  360.  *----------------------------------------------------------------------
  361.  */
  362.  
  363. static char *
  364. ForwBack(textPtr, string, lineIndexPtr, chPtr)
  365.     TkText *textPtr;        /* Information about widget that index
  366.                  * refers to. */
  367.     char *string;        /* String to parse for additional info
  368.                  * about modifier (count and units). 
  369.                  * Points to "+" or "-" that starts
  370.                  * modifier. */
  371.     int *lineIndexPtr;        /* Points to current line index, which will
  372.                  * be updated to reflect modifier. */
  373.     int *chPtr;            /* Points to current character index, which
  374.                  * will be updated to reflect modifier. */
  375. {
  376.     register char *p;
  377.     char *end, *units;
  378.     int count, length, lastLine;
  379.     TkTextLine *linePtr;
  380.  
  381.     /*
  382.      * Get the count (how many units forward or backward).
  383.      */
  384.  
  385.     p = string+1;
  386.     while (isspace(*p)) {
  387.     p++;
  388.     }
  389.     count = strtoul(p, &end, 0);
  390.     if (end == p) {
  391.     return NULL;
  392.     }
  393.     p = end;
  394.     while (isspace(*p)) {
  395.     p++;
  396.     }
  397.  
  398.     /*
  399.      * Find the end of this modifier (next space or + or - character),
  400.      * then parse the unit specifier and update the position
  401.      * accordingly.
  402.      */
  403.  
  404.     units = p; 
  405.     while ((*p != 0) && !isspace(*p) && (*p != '+') && (*p != '-')) {
  406.     p++;
  407.     }
  408.     length = p - units;
  409.     if ((*units == 'c') && (strncmp(units, "chars", length) == 0)) {
  410.     linePtr = TkTextRoundIndex(textPtr, lineIndexPtr, chPtr);
  411.     if (*string == '+') {
  412.         ForwardChars(textPtr, linePtr, lineIndexPtr, chPtr, count);
  413.     } else {
  414.         BackwardChars(textPtr, linePtr, lineIndexPtr, chPtr, count);
  415.     }
  416.     } else if ((*units == 'l') && (strncmp(units, "lines", length) == 0)) {
  417.     if (*string == '+') {
  418.         *lineIndexPtr += count;
  419.         lastLine = TkBTreeNumLines(textPtr->tree) - 1;
  420.         if (*lineIndexPtr > lastLine) {
  421.         *lineIndexPtr = lastLine;
  422.         }
  423.     } else {
  424.         *lineIndexPtr -= count;
  425.         if (*lineIndexPtr < 0) {
  426.         *lineIndexPtr = 0;
  427.         }
  428.     }
  429.     linePtr = TkBTreeFindLine(textPtr->tree, *lineIndexPtr);
  430.     if (*chPtr >= linePtr->numBytes) {
  431.         *chPtr = linePtr->numBytes - 1;
  432.     }
  433.     if (*chPtr < 0) {
  434.         *chPtr = 0;
  435.     }
  436.     } else {
  437.     return NULL;
  438.     }
  439.     return p;
  440. }
  441.  
  442. /*
  443.  *----------------------------------------------------------------------
  444.  *
  445.  * ForwardChars --
  446.  *
  447.  *    Given a position in a text widget, this procedure computes
  448.  *    a new position that is "count" characters ahead of the given
  449.  *    position.
  450.  *
  451.  * Results:
  452.  *    *LineIndexPtr and *chPtr are overwritten with new values
  453.  *    corresponding to the new position.
  454.  *
  455.  * Side effects:
  456.  *    None.
  457.  *
  458.  *----------------------------------------------------------------------
  459.  */
  460.  
  461.     /* ARGSUSED */
  462. static void
  463. ForwardChars(textPtr, linePtr, lineIndexPtr, chPtr, count)
  464.     TkText *textPtr;            /* Information about text widget. */
  465.     register TkTextLine *linePtr;    /* Text line corresponding to
  466.                      * *lineIndexPtr. */
  467.     int *lineIndexPtr;            /* Points to initial line index,
  468.                      * which is overwritten with final
  469.                      * line index. */
  470.     int *chPtr;                /* Points to initial character index,
  471.                      * which is overwritten with final
  472.                      * character index. */
  473.     int count;                /* How many characters forward to
  474.                      * move.  Must not be negative. */
  475. {
  476.     TkTextLine *nextPtr;
  477.     int bytesInLine;
  478.  
  479.     while (count > 0) {
  480.     bytesInLine = linePtr->numBytes - *chPtr;
  481.     if (bytesInLine > count) {
  482.         *chPtr += count;
  483.         return;
  484.     }
  485.     nextPtr = TkBTreeNextLine(linePtr);
  486.     if (nextPtr == NULL) {
  487.         *chPtr = linePtr->numBytes - 1;
  488.         return;
  489.     }
  490.     *chPtr = 0;
  491.     *lineIndexPtr += 1;
  492.     linePtr = nextPtr;
  493.     count -= bytesInLine;
  494.     }
  495. }
  496.  
  497. /*
  498.  *----------------------------------------------------------------------
  499.  *
  500.  * BackwardChars --
  501.  *
  502.  *    Given a position in a text widget, this procedure computes
  503.  *    a new position that is "count" characters earlier than the given
  504.  *    position.
  505.  *
  506.  * Results:
  507.  *    *LineIndexPtr and *chPtr are overwritten with new values
  508.  *    corresponding to the new position.
  509.  *
  510.  * Side effects:
  511.  *    None.
  512.  *
  513.  *----------------------------------------------------------------------
  514.  */
  515.  
  516. static void
  517. BackwardChars(textPtr, linePtr, lineIndexPtr, chPtr, count)
  518.     TkText *textPtr;            /* Information about text widget. */
  519.     register TkTextLine *linePtr;    /* Text line corresponding to
  520.                      * *lineIndexPtr. */
  521.     int *lineIndexPtr;            /* Points to initial line index,
  522.                      * which is overwritten with final
  523.                      * line index. */
  524.     int *chPtr;                /* Points to initial character index,
  525.                      * which is overwritten with final
  526.                      * character index. */
  527.     int count;                /* How many characters backward to
  528.                      * move.  Must not be negative. */
  529. {
  530.     int bytesInLine;
  531.  
  532.     while (count > 0) {
  533.     bytesInLine = *chPtr;
  534.     if (bytesInLine >= count) {
  535.         *chPtr -= count;
  536.         return;
  537.     }
  538.     if (*lineIndexPtr <= 0) {
  539.         *chPtr = 0;
  540.         return;
  541.     }
  542.     *lineIndexPtr -= 1;
  543.     linePtr = TkBTreeFindLine(textPtr->tree, *lineIndexPtr);
  544.     count -= bytesInLine;
  545.     *chPtr = linePtr->numBytes;
  546.     }
  547. }
  548.  
  549. /*
  550.  *----------------------------------------------------------------------
  551.  *
  552.  * StartEnd --
  553.  *
  554.  *    This procedure handles modifiers like "wordstart" and "lineend"
  555.  *    to adjust indices forwards or backwards.
  556.  *
  557.  * Results:
  558.  *    If the modifier is successfully parsed then the return value
  559.  *    is the address of the first character after the modifier, and
  560.  *    *lineIndexPtr and *chPtr are updated to reflect the modifier.
  561.  *    If there is a syntax error in the modifier then NULL is returned.
  562.  *
  563.  * Side effects:
  564.  *    None.
  565.  *
  566.  *----------------------------------------------------------------------
  567.  */
  568.  
  569. static char *
  570. StartEnd(textPtr, string, lineIndexPtr, chPtr)
  571.     TkText *textPtr;        /* Information about widget that index
  572.                  * refers to. */
  573.     char *string;        /* String to parse for additional info
  574.                  * about modifier (count and units). 
  575.                  * Points to first character of modifer
  576.                  * word. */
  577.     int *lineIndexPtr;        /* Points to current line index, which will
  578.                  * be updated to reflect modifier. */
  579.     int *chPtr;            /* Points to current character index, which
  580.                  * will be updated to reflect modifier. */
  581. {
  582.     char *p, c;
  583.     int length;
  584.     register TkTextLine *linePtr;
  585.  
  586.     /*
  587.      * Find the end of the modifier word.
  588.      */
  589.  
  590.     for (p = string; isalnum(*p); p++) {
  591.     /* Empty loop body. */
  592.     }
  593.     length = p-string;
  594.     linePtr = TkTextRoundIndex(textPtr, lineIndexPtr, chPtr);
  595.     if ((*string == 'l') && (strncmp(string, "lineend", length) == 0)
  596.         && (length >= 5)) {
  597.     *chPtr = linePtr->numBytes - 1;
  598.     } else if ((*string == 'l') && (strncmp(string, "linestart", length) == 0)
  599.         && (length >= 5)) {
  600.     *chPtr = 0;
  601.     } else if ((*string == 'w') && (strncmp(string, "wordend", length) == 0)
  602.         && (length >= 5)) {
  603.     c = linePtr->bytes[*chPtr];
  604.     if (!isalnum(c) && (c != '_')) {
  605.         if (*chPtr >= (linePtr->numBytes - 1)) {
  606.         /*
  607.          * End of line:  go to start of next line unless this is the
  608.          * last line in the text.
  609.          */
  610.  
  611.         if (TkBTreeNextLine(linePtr) != NULL) {
  612.             *lineIndexPtr += 1;
  613.             *chPtr = 0;
  614.         }
  615.         } else {
  616.         *chPtr += 1;
  617.         }
  618.     } else {
  619.         do {
  620.         *chPtr += 1;
  621.         c = linePtr->bytes[*chPtr];
  622.         } while (isalnum(c) || (c == '_'));
  623.     }
  624.     } else if ((*string == 'w') && (strncmp(string, "wordstart", length) == 0)
  625.         && (length >= 5)) {
  626.     c = linePtr->bytes[*chPtr];
  627.     if (isalnum(c) || (c == '_')) {
  628.         while (*chPtr > 0) {
  629.         c = linePtr->bytes[(*chPtr) - 1];
  630.         if (!isalnum(c) && (c != '_')) {
  631.             break;
  632.         }
  633.         *chPtr -= 1;
  634.         }
  635.     }
  636.     } else {
  637.     return NULL;
  638.     }
  639.     return p;
  640. }
  641.